#include "sdk.h"
#include <wx/progdlg.h>
#include "M_Code_auibook.h"
#include "filegroupsandmasks.h"

template<> ProjectManager* Mgr<ProjectManager>::instance = nullptr;
template<> bool  Mgr<ProjectManager>::isShutdown = false;
bool ProjectManager::s_CanShutdown = true;

BEGIN_EVENT_TABLE( ProjectManager, wxEvtHandler )
END_EVENT_TABLE()

class NullProjectManagerUI : public cbProjectManagerUI {
  public:
    M_Code_AuiNotebook* GetNotebook() { return nullptr; }
    M_Code_TreeCtrl* GetTree() { return nullptr; }
    void RebuildTree() {}
    void FreezeTree() {}
    void UnfreezeTree( bool force = false ) { ( void )force; }
    wxTreeItemId GetTreeSelection() { return wxTreeItemId(); }
    void UpdateActiveProject( M_Code_Project* ( oldProject ), M_Code_Project* ( newProject ), bool ( refresh ) ) {}
    void RemoveProject( M_Code_Project* ( project ) ) {}
    void BeginLoadingWorkspace() {}
    void CloseWorkspace() {}
    void FinishLoadingProject( M_Code_Project* ( project ), bool ( newAddition ), FilesGroupsAndMasks* ( fileGroups ) ) {}
    void FinishLoadingWorkspace( M_Code_Project* ( activeProject ), const wxString& ( workspaceTitle ) ) {}
    void ShowFileInTree( ProjectFile& ( projectFile ) ) {}
    bool QueryCloseAllProjects() { return true; }
    bool QueryCloseProject( M_Code_Project* ( proj ), bool dontsavefiles = false )  { ( void )dontsavefiles; return true; }
    bool QueryCloseWorkspace()  { return true; }
    int AskForBuildTargetIndex( M_Code_Project* project = nullptr ) { ( void )project; return -1; }
    wxArrayInt AskForMultiBuildTargetIndex( M_Code_Project* project = nullptr ) { ( void )project; return wxArrayInt(); }
    void ConfigureProjectDependencies( M_Code_Project* base = nullptr ) { ( void )base; }
    void SwitchToProjectsPage() {}
};

ProjectManager::ProjectManager() :
  m_ui( new NullProjectManagerUI ),
  m_pWorkspace( nullptr ),
  m_IsLoadingProject( false ),
  m_IsLoadingWorkspace( false ),
  m_IsClosingProject( false ),
  m_IsClosingWorkspace( false ),
  m_InitialDir( _T( "" ) ),
  m_CanSendWorkspaceChanged( false ),
  m_RunningPlugin( nullptr ) {
  m_InitialDir = wxFileName::GetCwd();
  m_pActiveProject = nullptr;
  m_pProjectToActivate = nullptr;
  m_pProjects = new ProjectsArray;
  m_pProjects->Clear();
  m_pFileGroups = new FilesGroupsAndMasks;
  Manager::Get()->RegisterEventSink( cbEVT_APP_STARTUP_DONE, new cbEventFunctor<ProjectManager, CodeBlocksEvent>( this, &ProjectManager::OnAppDoneStartup ) );
  Manager::Get()->GetAppWindow()->PushEventHandler( this );
}

ProjectManager::~ProjectManager() {
  delete m_pWorkspace;
  m_pWorkspace = nullptr;
  int count = m_pProjects->GetCount();
  for( int i = 0; i < count; ++i ) {
    M_Code_Project* project = m_pProjects->Item( i );
    if( project ) {
      delete project;
    }
  }
  m_pProjects->Clear();
  delete m_pProjects;
  m_pProjects = nullptr;
  delete m_pFileGroups;
  m_pFileGroups = nullptr;
  delete m_ui;
  m_ui = nullptr;
}

void ProjectManager::SetUI( cbProjectManagerUI *ui ) {
  delete m_ui;
  m_ui = ui;
}

wxString ProjectManager::GetDefaultPath() {
  wxString path = Manager::Get()->GetConfigManager( _T( "project_manager" ) )->Read( _T( "default_path" ), wxEmptyString );
  if( !path.IsEmpty() && path.Last() != _T( '/' ) && path.Last() != _T( '\\' ) ) {
    path.Append( wxFILE_SEP_PATH );
  }
  return path;
}

void ProjectManager::SetDefaultPath( const wxString& path ) {
  Manager::Get()->GetConfigManager( _T( "project_manager" ) )->Write( _T( "default_path" ), path );
}

bool ProjectManager::IsProjectStillOpen( M_Code_Project* project ) {
  int count = m_pProjects->GetCount();
  for( int i = 0; i < count; ++i ) {
    if( m_pProjects->Item( i ) == project ) {
      return true;
    }
  }
  return false;
}

void ProjectManager::SetProject( M_Code_Project* project, bool refresh ) {
  bool activeProjectChanged = false;
  if( project != m_pActiveProject ) {
    if( m_pWorkspace && m_pActiveProject ) {
      activeProjectChanged = true;
    }
  } else {
    return;
  }
  M_Code_Project *oldActiveProject = m_pActiveProject;
  m_pActiveProject = project;
  m_ui->UpdateActiveProject( oldActiveProject, m_pActiveProject, refresh );
  if( activeProjectChanged ) {
    m_pWorkspace->ActiveProjectChanged();
  }
  CodeBlocksEvent event( cbEVT_PROJECT_ACTIVATE );
  event.SetProject( m_pActiveProject );
  Manager::Get()->GetPluginManager()->NotifyPlugins( event );
}

M_Code_Project* ProjectManager::IsOpen( const wxString& filename ) {
  if( filename.IsEmpty() ) {
    return nullptr;
  }
  wxString realFile = realpath( filename );
  int count = m_pProjects->GetCount();
  for( int i = 0; i < count; ++i ) {
    M_Code_Project* project = m_pProjects->Item( i );
    if( project ) {
      if( project->GetFilename().Matches( realFile ) ) {
        return project;
      }
    }
  }
  return nullptr;
}

M_Code_Project* ProjectManager::LoadProject( const wxString& filename, bool activateIt ) {
  M_Code_Project* result = nullptr;
  if( !wxFileExists( filename ) || !BeginLoadingProject() ) {
    return nullptr;
  }
  do {
    M_Code_Project* project = IsOpen( filename );
    if( project ) {
      result = project;
      break;
    }
    if( FileTypeOf( filename ) == ftm_Code_Project ) {
      project = new M_Code_Project( filename );
      if( !project->IsLoaded() ) {
        delete project;
        break;
      }
      result = project;
    } else {
      EndLoadingProject( nullptr );
      cbMimePlugin* plugin = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile( filename );
      if( plugin ) {
        plugin->OpenFile( filename );
      }
    }
    break;
  }  while( false );
  EndLoadingProject( result );
  if( activateIt ) {
    if( m_IsLoadingWorkspace ) {
      m_pProjectToActivate = result;
    } else
    { SetProject( result, false ); }
  }
  return result;
}

void ProjectManager::ReloadProject( M_Code_Project *project ) {
  m_ui->FreezeTree();
  bool workspaceModified = m_pWorkspace ? m_pWorkspace->GetModified() : false;
  wxString name = project->GetFilename();
  wxString activeProjectName = m_pActiveProject ? m_pActiveProject->GetFilename() : wxString( wxEmptyString );
  ProjectsArray projectDependencies;
  ProjectsArray projectsDependingOnReloaded;
  for( DepsMap::iterator it = m_ProjectDeps.begin(); it != m_ProjectDeps.end(); ++it ) {
    if( !it->second ) {
      continue;
    }
    if( it->first == project ) {
      projectDependencies = *( it->second );
    } else {
      if( it->second->Index( project ) != wxNOT_FOUND ) {
        projectsDependingOnReloaded.push_back( it->first );
      }
    }
  }
  int originalPosition = m_pProjects->Index( project );
  CloseProject( project );
  M_Code_Project *loadedProject = LoadProject( name );
  if( loadedProject ) {
    if( !projectDependencies.empty() ) {
      for( ProjectsArray::iterator it = projectDependencies.begin(); it != projectDependencies.end(); ++it ) {
        AddProjectDependency( loadedProject, *it );
      }
    }
    if( !projectsDependingOnReloaded.empty() ) {
      for( ProjectsArray::iterator it = projectsDependingOnReloaded.begin();
           it != projectsDependingOnReloaded.end(); ++it ) {
        AddProjectDependency( *it, loadedProject );
      }
    }
    int loadedPosition = -1;
    int index = 0;
    M_Code_Project *projectToActivate = nullptr;
    for( ProjectsArray::iterator it = m_pProjects->begin(); it != m_pProjects->end(); ++it, ++index ) {
      if( *it == loadedProject ) {
        loadedPosition = index;
      }
      if( ( *it )->GetFilename() == activeProjectName ) {
        projectToActivate = *it;
      }
    }
    m_pProjects->RemoveAt( loadedPosition );
    m_pProjects->Insert( loadedProject, originalPosition );
    if( projectToActivate ) {
      m_pActiveProject = projectToActivate;
    }
    m_ui->RebuildTree();
    if( m_pWorkspace ) {
      m_pWorkspace->SetModified( workspaceModified );
    }
  }
  m_ui->UnfreezeTree();
}

M_Code_Project* ProjectManager::NewProject( const wxString& filename ) {
  if( !filename.IsEmpty() && wxFileExists( filename ) ) {
    if( msging( _T( "项目文件已存在覆盖它吗?" ),
                _T( "审查" ), wxYES_NO | wxICON_QUESTION ) == wxID_YES ) {
      if( !wxRemoveFile( filename ) ) {
        msging( _T( "无法删除旧项目文件以创建新项目文件 该文件可能是只读的?!" ),
                _T( "错误" ), wxICON_WARNING );
        return nullptr;
      }
    } else
    { return nullptr; }
  }
  M_Code_Project* prj = IsOpen( filename );
  if( !prj && BeginLoadingProject() ) {
    prj = new M_Code_Project( filename );
    EndLoadingProject( prj );
    SetProject( prj, !m_IsLoadingWorkspace );
  }
  return prj;
}

bool ProjectManager::CloseAllProjects( bool dontsave ) {
  if( !dontsave ) {
    if( !m_ui->QueryCloseAllProjects() ) {
      return false;
    }
  }
  m_ui->FreezeTree();
  m_IsClosingProject = true;
  while( m_pProjects->GetCount() != 0 ) {
    if( !CloseProject( m_pProjects->Item( 0 ), true, false ) ) {
      m_ui->UnfreezeTree( true );
      m_IsClosingProject = false;
      return false;
    }
  }
  if( !Manager::IsAppShuttingDown() ) {
    m_ui->RebuildTree();
  }
  m_ui->UnfreezeTree( true );
  if( !m_InitialDir.IsEmpty() ) {
    wxFileName::SetCwd( m_InitialDir );
  }
  m_IsClosingProject = false;
  WorkspaceChanged();
  return true;
}

bool ProjectManager::CloseProject( M_Code_Project* project, bool dontsave, bool refresh ) {
  if( !project ) {
    return true;
  }
  if( project->GetCurrentlyCompilingTarget() ) {
    return false;
  }
  if( !dontsave ) {
    if( !m_ui->QueryCloseProject( project ) ) {
      return false;
    }
  }
  bool wasActive = project == m_pActiveProject;
  if( wasActive ) {
    m_pActiveProject = nullptr;
  }
  int index = m_pProjects->Index( project );
  if( index == wxNOT_FOUND ) {
    return false;
  }
  bool isClosingOtherProjects = m_IsClosingProject;
  m_IsClosingProject = true;
  Manager::Get()->GetEditorManager()->UpdateProjectFiles( project );
  project->SaveLayout();
  if( m_pWorkspace ) {
    m_pWorkspace->SetModified( true );
  }
  RemoveProjectFromAllDependencies( project );
  m_pProjects->Remove( project );
  CodeBlocksEvent event( cbEVT_PROJECT_CLOSE );
  event.SetProject( project );
  Manager::Get()->GetPluginManager()->NotifyPlugins( event );
  project->CloseAllFiles( true );
  if( refresh ) {
    m_ui->RemoveProject( project );
  }
  if( wasActive && m_pProjects->GetCount() ) {
    SetProject( m_pProjects->Item( 0 ), refresh );
  }
  delete project;
  if( !m_InitialDir.IsEmpty() ) {
    wxFileName::SetCwd( m_InitialDir );
  }
  m_IsClosingProject = isClosingOtherProjects;
  WorkspaceChanged();
  return true;
}

bool ProjectManager::CloseActiveProject( bool dontsave ) {
  if( !CloseProject( m_pActiveProject, dontsave ) ) {
    return false;
  }
  if( m_pProjects->GetCount() > 0 ) {
    SetProject( m_pProjects->Item( 0 ) );
  }
  return true;
}

bool ProjectManager::SaveProject( M_Code_Project* project ) {
  if( !project ) {
    return false;
  }
  if( project->Save() ) {
    m_ui->RebuildTree();
    return true;
  }
  return false;
}

bool ProjectManager::SaveProjectAs( M_Code_Project* project ) {
  if( !project ) {
    return false;
  }
  if( project->SaveAs() ) {
    m_ui->RebuildTree();
    return true;
  }
  return false;
}

bool ProjectManager::SaveActiveProject() {
  return SaveProject( m_pActiveProject );
}

bool ProjectManager::SaveActiveProjectAs() {
  return SaveProjectAs( m_pActiveProject );
}

bool ProjectManager::SaveAllProjects() {
  m_ui->FreezeTree();
  int prjCount = m_pProjects->GetCount();
  int count = 0;
  for( int i = 0; i < prjCount; ++i ) {
    M_Code_Project* project = m_pProjects->Item( i );
    if( project ) {
      bool isModified = project->GetModified();
      if( isModified && SaveProject( project ) ) {
        ++count;
      }
    }
  }
  m_ui->UnfreezeTree( true );
  return count == prjCount;
}

M_Code_Workspace* ProjectManager::GetWorkspace() {
  if( !m_pWorkspace ) {
    m_pWorkspace = new M_Code_Workspace( _T( "" ) );
    m_pWorkspace->SetTitle( _T( "工作空间" ) );
    m_pWorkspace->SetModified( false );
  }
  return m_pWorkspace;
}


bool ProjectManager::LoadWorkspace( const wxString& filename ) {
  if( !BeginLoadingWorkspace() ) {
    return false;
  }
  m_pWorkspace = new M_Code_Workspace( filename );
  EndLoadingWorkspace();
  if( m_pProjects->GetCount() > 0 && !m_pActiveProject ) {
    SetProject( m_pProjects->Item( 0 ), false );
  }
  if( m_pWorkspace && m_pWorkspace->IsOK() ) {
    CodeBlocksEvent event( cbEVT_WORKSPACE_LOADING_COMPLETE );
    Manager::Get()->GetPluginManager()->NotifyPlugins( event );
    return true;
  }
  return false;
}

bool ProjectManager::SaveWorkspace() {
  return GetWorkspace()->Save();
}

bool ProjectManager::SaveWorkspaceAs( const wxString& filename ) {
  return GetWorkspace()->SaveAs( filename );
}

bool ProjectManager::CloseWorkspace() {
  bool result = false;
  m_IsClosingWorkspace = true;
  CodeBlocksEvent eventBegin( cbEVT_WORKSPACE_CLOSING_BEGIN );
  Manager::Get()->GetPluginManager()->NotifyPlugins( eventBegin );
  if( m_pWorkspace ) {
    if( !Manager::IsBatchBuild() && !m_ui->QueryCloseWorkspace() ) {
      m_IsClosingWorkspace = false;
      return false;
    }
    if( !CloseAllProjects( true ) ) {
      m_IsClosingWorkspace = false;
      return false;
    }
    delete m_pWorkspace;
    m_pWorkspace = nullptr;
    m_ui->CloseWorkspace();
    result = true;
  } else {
    result = CloseAllProjects( false );
  }
  m_IsClosingWorkspace = false;
  CodeBlocksEvent eventComplete( cbEVT_WORKSPACE_CLOSING_COMPLETE );
  Manager::Get()->GetPluginManager()->NotifyPlugins( eventComplete );
  WorkspaceChanged();
  return result;
}

bool ProjectManager::IsBusy() {
  if( Manager::IsAppShuttingDown() ) {
    return true;
  }
  ProjectManager* projman = Manager::Get()->GetProjectManager();
  if( !projman ) {
    return true;
  }
  return projman->IsLoadingOrClosing();
}

bool ProjectManager::IsLoadingOrClosing() {
  return ( m_IsLoadingProject || m_IsLoadingWorkspace || m_IsClosingProject || m_IsClosingWorkspace );
}

bool ProjectManager::IsLoadingProject() {
  return m_IsLoadingProject;
}

bool ProjectManager::IsLoadingWorkspace() {
  return m_IsLoadingWorkspace;
}

bool ProjectManager::IsLoading() {
  return ( m_IsLoadingProject || m_IsLoadingWorkspace );
}

bool ProjectManager::IsClosingProject() {
  return m_IsClosingProject;
}

bool ProjectManager::IsClosingWorkspace() {
  return m_IsClosingWorkspace;
}


int ProjectManager::DoAddFileToProject( const wxString& filename, M_Code_Project* project, wxArrayInt& targets ) {
  if( !project ) {
    return 0;
  }
  if( targets.GetCount() == 0 ) {
    if( project->GetBuildTargetsCount() == 1 ) {
      targets.Add( 0 );
    } else {
      targets = m_ui->AskForMultiBuildTargetIndex( project );
      if( targets.GetCount() == 0 ) {
        return 0;
      }
    }
  }
  wxFileName fname( filename );
  fname.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE, project->GetBasePath() );
  fname.MakeRelativeTo( project->GetBasePath() );
  ProjectFile* pf = project->AddFile( -1, fname.GetFullPath() );
  if( pf ) {
    for( size_t i = 0; i < targets.GetCount(); ++i ) {
      ProjectBuildTarget* target = project->GetBuildTarget( targets[i] );
      if( target ) {
        pf->AddBuildTarget( target->GetTitle() );
      }
    }
  }
  return targets.GetCount();
}

int ProjectManager::AddFileToProject( const wxString& filename, M_Code_Project* project, int target ) {
  if( !project ) {
    project = GetActiveProject();
  }
  wxArrayInt targets;
  targets.Add( target );
  if( AddFileToProject( filename, project, targets ) == 1 ) {
    return targets[0];
  }
  return -1;
}

int ProjectManager::AddFileToProject( const wxString& filename, M_Code_Project* project, wxArrayInt& targets ) {
  if( !project ) {
    project = GetActiveProject();
  }
  int ret = DoAddFileToProject( filename, project, targets );
  if( ret > 0 ) {
    CodeBlocksEvent event( cbEVT_PROJECT_FILE_ADDED );
    event.SetProject( project );
    event.SetString( filename );
    Manager::Get()->GetPluginManager()->NotifyPlugins( event );
  }
  return ret;
}

int ProjectManager::AddMultipleFilesToProject( const wxArrayString& filelist, M_Code_Project* project, int target ) {
  if( !project ) {
    project = GetActiveProject();
  }
  wxArrayInt targets;
  targets.Add( target );
  if( AddMultipleFilesToProject( filelist, project, targets ) == 1 ) {
    return targets[0];
  }
  return -1;
}

int ProjectManager::AddMultipleFilesToProject( const wxArrayString& filelist, M_Code_Project* project, wxArrayInt& targets ) {
  wxProgressDialog progress( _T( "项目管理" ), _T( "正在向项目添加文件，请稍候..." ), filelist.GetCount(), Manager::Get()->GetAppFrame() );
  if( !project ) {
    project = GetActiveProject();
  }
  if( project ) {
    project->BeginAddFiles();
    wxArrayString addedFiles;
    for( unsigned int i = 0; i < filelist.GetCount(); ++i ) {
      if( DoAddFileToProject( filelist[i], project, targets ) != 0 ) {
        addedFiles.Add( filelist[i] );
      }
      progress.Update( i );
    }
    if( addedFiles.GetCount() != 0 ) {
      for( unsigned int i = 0; i < addedFiles.GetCount(); ++i ) {
        CodeBlocksEvent event( cbEVT_PROJECT_FILE_ADDED );
        event.SetProject( project );
        event.SetString( addedFiles[i] );
        Manager::Get()->GetPluginManager()->NotifyPlugins( event );
      }
    }
    project->EndAddFiles();
  }
  return targets.GetCount();
}

bool ProjectManager::CausesCircularDependency( M_Code_Project* base, M_Code_Project* dependsOn ) {
  if( !base || !dependsOn ) {
    return false;
  }
  if( base == dependsOn ) {
    return true;
  }
  const ProjectsArray* arr = GetDependenciesForProject( dependsOn );
  if( arr ) {
    for( size_t i = 0; i < arr->GetCount(); ++i ) {
      if( CausesCircularDependency( base, arr->Item( i ) ) ) {
        return true;
      }
    }
  }
  return false;
}

bool ProjectManager::AddProjectDependency( M_Code_Project* base, M_Code_Project* dependsOn ) {
  if( !base || !dependsOn ) {
    return false;
  }
  if( CausesCircularDependency( base, dependsOn ) ) {
    return false;
  }
  ProjectsArray* arr = nullptr;
  DepsMap::iterator it = m_ProjectDeps.find( base );
  if( it == m_ProjectDeps.end() ) {
    arr = new ProjectsArray;
    m_ProjectDeps[base] = arr;
  } else {
    arr = it->second;
  }
  if( arr && arr->Index( dependsOn ) == wxNOT_FOUND ) {
    arr->Add( dependsOn );
    if( m_pWorkspace ) {
      m_pWorkspace->SetModified( true );
    }
    Manager::Get()->GetLogManager()->DebugLog( F( _T( "%s 现在取决于 %s (%lu deps)" ), base->GetTitle().wx_str(), dependsOn->GetTitle().wx_str(), static_cast<unsigned long>( arr->GetCount() ) ) );
  }
  return true;
}

void ProjectManager::RemoveProjectDependency( M_Code_Project* base, M_Code_Project* doesNotDependOn ) {
  if( !base || !doesNotDependOn ) {
    return;
  }
  DepsMap::iterator it = m_ProjectDeps.find( base );
  if( it == m_ProjectDeps.end() ) {
    return;
  }
  ProjectsArray* arr = it->second;
  arr->Remove( doesNotDependOn );
  Manager::Get()->GetLogManager()->Log( F( _T( "%s 现在不依赖 %s (%lu deps)" ), base->GetTitle().wx_str(), doesNotDependOn->GetTitle().wx_str(), static_cast<unsigned long>( arr->GetCount() ) ) );
  if( !arr->GetCount() ) {
    m_ProjectDeps.erase( it );
    delete arr;
  }
  if( m_pWorkspace ) {
    m_pWorkspace->SetModified( true );
  }
}

void ProjectManager::ClearProjectDependencies( M_Code_Project* base ) {
  if( !base ) {
    return;
  }
  DepsMap::iterator it = m_ProjectDeps.find( base );
  if( it == m_ProjectDeps.end() ) {
    return;
  }
  delete it->second;
  m_ProjectDeps.erase( it );
  if( m_pWorkspace ) {
    m_pWorkspace->SetModified( true );
  }
  Manager::Get()->GetLogManager()->Log( _T( "Removed all deps from " ) + base->GetTitle() );
}

void ProjectManager::RemoveProjectFromAllDependencies( M_Code_Project* base ) {
  if( !base ) {
    return;
  }
  DepsMap::iterator it = m_ProjectDeps.begin();
  while( it != m_ProjectDeps.end() ) {
    if( it->first == base ) {
      ++it;
      continue;
    }
    ProjectsArray* arr = it->second;
    if( !arr ) {
      ++it;
      continue;
    }
    int index = arr->Index( base );
    if( index != wxNOT_FOUND ) {
      arr->RemoveAt( index );
    }
    if( m_pWorkspace ) {
      m_pWorkspace->SetModified( true );
    }
    if( !arr->GetCount() ) {
      DepsMap::iterator it2 = it++;
      m_ProjectDeps.erase( it2 );
      delete arr;
    } else
    { ++it; }
  }
  Manager::Get()->GetLogManager()->Log( F( _T( "Removed %s from all deps" ), base->GetTitle().wx_str() ) );
}

const ProjectsArray* ProjectManager::GetDependenciesForProject( M_Code_Project* base ) {
  DepsMap::iterator it = m_ProjectDeps.find( base );
  if( it != m_ProjectDeps.end() ) {
    return it->second;
  }
  return nullptr;
}


void ProjectManager::OnAppDoneStartup( CodeBlocksEvent& event ) {
  m_CanSendWorkspaceChanged = true;
  event.Skip();
}

void ProjectManager::WorkspaceChanged() {
  if( !IsBusy() && m_CanSendWorkspaceChanged ) {
    CodeBlocksEvent event( cbEVT_WORKSPACE_CHANGED );
    Manager::Get()->GetPluginManager()->NotifyPlugins( event );
    Manager::Get()->GetEditorManager()->GetNotebook()->MinimizeFreeSpace();
  }
}

void ProjectManager::RemoveFileFromProject( ProjectFile *pfile, M_Code_Project* project ) {
  if( !pfile ) {
    Manager::Get()->GetLogManager()->LogError( _T( "项目文件无效！" ) );
    return;
  }
  if( pfile->AutoGeneratedBy() ) {
    InfoWindow::Tip( _T( "无法删除文件因为它是自动生成的" ), _T( "错误" ), 3500 );
    return;
  }
  if( !project ) {
    project = pfile->GetParentProject();
  }
  wxString filename = pfile->file.GetFullPath();
  project->RemoveFile( pfile );
  CodeBlocksEvent evt( cbEVT_PROJECT_FILE_REMOVED );
  evt.SetProject( project );
  evt.SetString( filename );
  Manager::Get()->GetPluginManager()->NotifyPlugins( evt );
  Manager::Get()->GetLogManager()->DebugLog( _T( "Removed " ) + filename + _T( " from " ) + project->GetTitle() );
}

bool ProjectManager::BeginLoadingProject() {
  if( m_IsLoadingProject ) {
    return false;
  }
  if( !Manager::Get()->GetPluginManager()->FindPluginByName( _T( "Compiler" ) ) ) {
    InfoWindow::Tip( _T( "打开一个项目必须先激活编译器插件" ), _T( "错误" ), 3500 );
    return false;
  }
  s_CanShutdown = false;
  m_IsLoadingProject = true;
  return true;
}

void ProjectManager::EndLoadingProject( M_Code_Project* project ) {
  s_CanShutdown = true;
  if( !m_IsLoadingProject ) {
    return;
  }
  if( project ) {
    bool newAddition = m_pProjects->Index( project ) == -1;
    if( newAddition ) {
      m_pProjects->Add( project );
      project->LoadLayout();
    }
    if( !m_IsLoadingWorkspace ) {
      m_ui->FinishLoadingProject( project, newAddition, m_pFileGroups );
    }
    if( m_pWorkspace ) {
      m_pWorkspace->SetModified( true );
    }
    if( !m_IsLoadingWorkspace ) {
      CodeBlocksEvent event( cbEVT_PROJECT_OPEN );
      event.SetProject( project );
      Manager::Get()->ProcessEvent( event );
      if( project->GetShowNotesOnLoad() ) {
        project->ShowNotes( true );
      }
    }
  }
  m_IsLoadingProject = false;
  if( !m_IsLoadingWorkspace ) {
    Manager::Get()->GetUserVariableManager()->Arrogate();
  }
  WorkspaceChanged();
}

bool ProjectManager::BeginLoadingWorkspace() {
  if( m_IsLoadingWorkspace ) {
    return false;
  }
  m_IsLoadingWorkspace = true;
  if( !CloseWorkspace() ) {
    m_IsLoadingWorkspace = false;
    return false;
  }
  m_ui->BeginLoadingWorkspace();
  return true;
}

void ProjectManager::EndLoadingWorkspace() {
  if( !m_IsLoadingWorkspace ) {
    return;
  }
  m_IsLoadingWorkspace = false;
  if( !m_pWorkspace ) {
    return;
  }
  if( m_pWorkspace->IsOK() ) {
    if( m_pProjectToActivate ) {
      SetProject( m_pProjectToActivate, true );
      m_pProjectToActivate = nullptr;
    }
    m_ui->FinishLoadingWorkspace( m_pActiveProject, m_pWorkspace->GetTitle() );
    Manager::Get()->GetUserVariableManager()->Arrogate();
    int numNotes = 0;
    for( size_t i = 0; i < m_pProjects->GetCount(); ++i ) {
      M_Code_Project* project = m_pProjects->Item( i );
      CodeBlocksEvent event( cbEVT_PROJECT_OPEN );
      event.SetProject( project );
      Manager::Get()->GetPluginManager()->NotifyPlugins( event );
      if( project->GetShowNotesOnLoad() && !project->GetNotes().IsEmpty() ) {
        ++numNotes;
      }
    }
    if( numNotes ) {
      if( numNotes == 1 ||
          msging( F( _T( "%d projects contain notes that should be displayed on-load.\n"
                         "Do you want to display them now, one after the other?" ),
                     numNotes ),
                  _T( "Display project notes?" ),
                  wxICON_QUESTION | wxYES_NO ) == wxID_YES ) {
        for( size_t i = 0; i < m_pProjects->GetCount(); ++i ) {
          M_Code_Project* project = m_pProjects->Item( i );
          if( project->GetShowNotesOnLoad() ) {
            project->ShowNotes( true );
          }
        }
      }
    }
    WorkspaceChanged();
  } else {
    CloseWorkspace();
  }
}

void ProjectManager::SetIsRunning( M_Code_Plugin *plugin ) {
  m_RunningPlugin = plugin;
}

M_Code_Plugin* ProjectManager::GetIsRunning() const {
  return m_RunningPlugin;
}

M_Code_Project* ProjectManager::FindProjectForFile( const wxString& file, ProjectFile **resultFile,
    bool isRelative, bool isUnixFilename ) {
  for( size_t i = 0; i < m_pProjects->GetCount(); ++i ) {
    M_Code_Project* prj = m_pProjects->Item( i );
    ProjectFile *temp = prj->GetFileByFilename( file, isRelative, isUnixFilename );
    if( temp ) {
      if( resultFile ) {
        *resultFile = temp;
      }
      return prj;
    }
  }
  if( resultFile ) {
    *resultFile = nullptr;
  }
  return nullptr;
}
